iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 16
0
Mobile Development

大一之 Android Kotlin 自習心路歷程系列 第 16

[Day 16] Android in Kotlin: 自定義 Dialog 分享

  • 分享至 

  • xImage
  •  

以下出現之畫面皆為參照 FieC 公司開發的 Ahorro 所仿造出來的,該創意全為該公司所有,且本文之全部內容都與其公司無任何直接關係。

Ahorro - 輕鬆記帳,簡單理財

在設計 app 的時候,常會有跳出對話框的時候,而此時,通常也不會去使用內建的對話框,會依照該 app 的需求,去使用自定義的 dialog。比起內建的,自定義可以更方便的去新增更多元件,更改顏色、字型、背景等等,能依照我們的需求像之前一樣把 layout 寫好再套用。

像這個
dialog

在判斷出資料未填完就要離開時:按下確定會離開;按下取消會留下。
完成一個簡單的 yes or no dialog

XML

這個 dialog 我只做兩個 button 跟一個 text view 顯示內容
dialog layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    android:background="@color/AhorroBackground_YellowLight"
    android:layout_gravity="center">
    <TextView
        android:id="@+id/dialog_yes_no_message"
        android:text="訊息"
        android:textColor="@color/AhorroTextColor"
        android:layout_margin="30dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/dialog_negative"
            android:text="否"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_marginBottom="20dp"
            android:layout_marginStart="30dp"
            android:layout_marginEnd="30dp"
            android:background="@drawable/ahorro_button_dialog"
            android:textColor="@color/AhorroWhite"/>
        <Button
            android:id="@+id/dialog_positive"
            android:text="是"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_marginBottom="20dp"
            android:layout_marginStart="30dp"
            android:layout_marginEnd="30dp"
            android:background="@drawable/ahorro_button_dialog"
            android:textColor="@color/AhorroWhite"/>
    </LinearLayout>

</LinearLayout>

其中的按鈕的 background 是之前先做好的 drawable

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item  android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/AhorroDialog_GrayDark"/>
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/AhorroDialog_Gray"/>
        </shape>
    </item>
</selector>

在按下時,顏色會變深

Class

新增一個 class 繼承於 Dialog(),並用建構元把 context 丟給 dialog
override onCreate()方法,記得還要用 setContentView 把我們剛剛寫的 layout 畫面給他

class YesNoDialog(context: Context): Dialog(context){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.dialog_yes_no)
        
        
    }
}

在剛剛的圖片中,需要有一個 String 裝內容,跟兩個 button 的點擊監聽器接口
所以寫出各個的 setter。

之所以我們前面的 dialog 可以在 builder 後面一直小數點就可以繼續增加屬性,是因為他有回傳的關係,來看看原本 builder 的其中一個 function,

/**
 * Set the title displayed in the {@link Dialog}.
 *
 * @return This Builder object to allow for chaining of calls to set methods
 */
public Builder setTitle(@Nullable CharSequence title) {
    P.mTitle = title;
    return this;
}

他的確是把整個 builder 在回傳回去的,那們我們也就仿造它的寫法

完成以後會像這樣

class YesNoDialog(context: Context): Dialog(context){

    private var message: String?= null

    private var cancelListener: IOnCancelListener? = null
    private var confirmListener: IOnConfirmListener? = null

    fun setMessage(message: String?): YesNoDialog {
        this.message = message
        return this
    }

    fun setConfirm(Listener: IOnConfirmListener): YesNoDialog {
        this.confirmListener = Listener
        return this
    }

    fun setCancel(Listener: IOnCancelListener): YesNoDialog {
        this.cancelListener = Listener
        return this
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.dialog_yes_no)

        val btNegative: Button= findViewById(R.id.dialog_negative)
        val btPositive: Button= findViewById(R.id.dialog_positive)
        val tvContent: TextView= findViewById(R.id.dialog_yes_no_message)

        message?.let {
            tvContent.text= it
        }

        btPositive.setOnClickListener(this::clickListener)
        btNegative.setOnClickListener(this::clickListener)
    }

    private fun clickListener(v: View){
        when(v.id){
            R.id.dialog_positive -> {
                confirmListener?.let {
                    it.onConfirm(this)
                }
            }
            R.id.dialog_negative -> {
                cancelListener?.let {
                    it.onCancel(this)
                }
            }
        }
    }

    interface IOnCancelListener {
        fun onCancel(dialog: YesNoDialog?)
    }

    interface IOnConfirmListener {
        fun onConfirm(dialog: YesNoDialog?)
    }
}

使用

在使用上,因為一個 activity 或 fragment 可能會多次使用,我就把它分出一個 class。

private fun showDialog(message: String){
    val yesNoDialog= YesNoDialog(this)
        yesNoDialog
            .setMessage(message)
            .setCancel(object : YesNoDialog.IOnCancelListener {
                override fun onCancel(dialog: YesNoDialog?) {
                    yesNoDialog.dismiss()
                }
            })
            .setConfirm(object : YesNoDialog.IOnConfirmListener {
                override fun onConfirm(dialog: YesNoDialog?) {
                    finish()
                }
            }).show()
}

其實用起來跟一般的 dialog 差不多,但畫面的靈活度就高上許多,往後應該也會經常用到類似的功能。


上一篇
[Day 15] Android in Kotlin: 其他的 Dialog 屬性分享
下一篇
[Day 17] Android in Kotlin: Bottom Sheet 分享
系列文
大一之 Android Kotlin 自習心路歷程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言